package com.tc.data.datasource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

import javax.annotation.PostConstruct;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.sql.DataSource;
import java.lang.management.ManagementFactory;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 读写数据源
 * 
 * @author Anders Zhu
 * 
 */
public class ReadWriteDataSource extends AbstractRoutingDataSource implements DisposableBean, ReadWriteDataSourceMBean {

	private static final Logger LOGGER = LoggerFactory.getLogger(ReadWriteDataSource.class);

	private ReadWriteDataSourceKey dataSourceKey;
	private Map<String, String> markDownMap = new ConcurrentHashMap<String, String>();
	private Map<String, DataSource> dataSources;
	private String matrixName;
	private String groupName;
	private Map<Object, Object> prehotReadDataSource;

	@PostConstruct
	public void register() {
		synchronized (this) {
			MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
			ObjectName objectName;
			try {
				String classInfo = this.toString() + "|" + this.dataSourceKey.getWriteKey();
				objectName = new ObjectName("com.vip.venus.data:type=" + classInfo);
				if (!mbeanServer.isRegistered(objectName)) {
					mbeanServer.registerMBean(this, objectName);
					if (LOGGER.isDebugEnabled()) {
						LOGGER.debug("com.vip.venus.data:type=" + classInfo + " registered successfully");
					}
				}
			} catch (Exception e) {
				LOGGER.error(e.getMessage());
			} 
		}
	}
	@Override
	public void setTargetDataSources(Map<Object, Object> targetDataSources) {
		this.prehotReadDataSource = targetDataSources;
		super.setTargetDataSources(targetDataSources);
	}
	
	public Map<Object, Object> getPrehotWriteDataSource() {
		return prehotReadDataSource;
	}

	
	public DataSource resolveSpecifiedDataSource(Object dataSource)
			throws IllegalArgumentException {
		return super.resolveSpecifiedDataSource(dataSource);
	}
	
	@Override
	public void putKey(String key, String replaceKey) {
		this.markDownMap.put(key, replaceKey);
	}

	@Override
	public void removeKey(String key) {
		this.markDownMap.remove(key);
	}

	@Override
	public String getWriteKey() {
		return getDataSourceKey().getWriteKey();
	}

	@Override
	public Map<String, String> getReadKeys() {
		return getDataSourceKey().getReadKeys();
	}

	@Override
	public Map<String, String> getMarkDownKeys() {
		return markDownMap;
	}

	@Override
	protected Object determineCurrentLookupKey() {
		String key = dataSourceKey.getKey();
		LOGGER.debug(">>>>>>>>>> determin current read/write key is {}", key);

		return key;
	}

	@Override
	public Connection getConnection() throws SQLException {
		return getConnectionFromDataSource(null, null);
	}

	@Override
	public Connection getConnection(String username, String password) throws SQLException {
		return getConnectionFromDataSource(username, password);
	}

	private Connection getConnectionFromDataSource(String username, String password) throws SQLException {
		Connection connection = null;
		DataSource ds = determineTargetDataSource();
		try {
			if (username == null && password == null) {
				connection = ds.getConnection();
			} else {
				connection = ds.getConnection(username, password);
			}
		} catch (Exception e) {
			String key = (String) determineCurrentLookupKey();

			LOGGER.error("failed to get connection from key["+ key +"]");
			throw new SQLException(e.getMessage());
		}
		return connection;
	}


	@Override
	public void destroy() throws Exception {}

	public ReadWriteDataSourceKey getDataSourceKey() {
		return dataSourceKey;
	}

	public void setDataSourceKey(ReadWriteDataSourceKey dataSourceKey) {
		this.dataSourceKey = dataSourceKey;
	}

	public Map<String, DataSource> getDataSources() {
		return dataSources;
	}

	public void setDataSources(Map<String, DataSource> dataSources) {
		this.dataSources = dataSources;
	}

	public String getMatrixName() {
		return matrixName;
	}

	public void setMatrixName(String matrixName) {
		this.matrixName = matrixName;
	}

	public String getGroupName() {
		return groupName;
	}

	public void setGroupName(String groupName) {
		this.groupName = groupName;
	}

	@Override
	public void afterPropertiesSet() {
		super.afterPropertiesSet();

		//MetricRegister.registerMetric(this, matrixName, groupName, dataSources);
	}

}
